package libs.game;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;

/**
 *              This class is design to be a parent of game solvers.
 *
 * @author      Jan Papousek
 * @version     2009-03-29
 */
public abstract class AbstractSolver implements Solver {


    /**
     *          It returns a comparator which is needed to set a priority
     *          of game plans when the solver looks for the solution.
     *
     * @return  Comparator.
     */
    protected abstract Comparator<GamePlan> getComparator();

    /**
     *          It checks if the game plan is locked.
     *
     * @param   plan            The game plan.
     * @return  TRUE if the plan is locked, otherwise FALSE.
     */
    protected abstract boolean isLocked(GamePlan plan);

    /**
     *          It makes all possibles moves from this situation.
     * @param   plan            The situation.
     * @return  All possible moves.
     * @throws  NullPointerException if the plan is NULL.
     */
    protected abstract Collection<GamePlan> move(GamePlan plan);

    /**
     *          This method checks equalency of the game plans and looks for differences.
     *
     * @param   situation1      The original situation.
     * @param   situation2      The target situation.
     * @throws  NullPointerException if some of the situation are NULL.
     * @throws  IllegalArgumentException if the situation has wrong format.
     */
    protected abstract void processSituations(GamePlan situation1, GamePlan situation2);

    @Override
    public int isSoluble(GamePlan situation1, GamePlan situation2) {
        // The first I check the situations and find the difference.
        processSituations(situation1, situation2);

        // The states where the solver looks for solution.
        PriorityQueue<GamePlan> states = new PriorityQueue<GamePlan>(limit(),getComparator());
        states.add(situation1);

        // Used states which the solver does not need.
        Set<GamePlan> used = new HashSet<GamePlan>(limit());
        used.add(situation1);

        // Searching
        int steps = 0;
        GamePlan currentPlan;
        while((currentPlan = states.poll()) != null) {
            // Positive result.
            if (currentPlan.equals(situation2)) {
                return 1;
            }
            for (GamePlan newPlan : move(currentPlan)) {
                if (used.add(newPlan)) {
                    if (!isLocked(newPlan)) {
                        states.add(newPlan);
                    }
                }
            }
            steps++;
            if (steps >= limit()) {
                // The solver does not know.
                return 0;
            }
        }
        // Negative result.
        return (-1);
    }

    @Override
    public int[] solve(GamePlan situation1, GamePlan situation2) throws SolutionException {
        throw new UnsupportedOperationException();
    }

}
